local Class = require("Core.Class")
local LuaBehaviour = require("Core.LuaBehaviour")
local ResManager = require('Game.Manager.ResManager')
local GameManager = require("Game.Manager.GameManager")
local EventManager = require("Game.Manager.EventManager")
local Coroutine = require("Core.Coroutine")
local UnityUtils = require("Game.Util.UnityUtils")

--- @Class EmenyBase
local EmenyBase = Class("EmenyBase", LuaBehaviour)

function EmenyBase:__init()
    -- 常数
    self.IS_CAN_MOVE_ID = CSE.Animator.StringToHash('IsCanMove')
    self.IS_DIE_ID = CSE.Animator.StringToHash('IsDie')

    self.ROTATE_LAYER_MASK = CS.Util.GameUtil.OrLayerMask('Map', 'Emeny')

    -- 基础属性
    self.hp = 1                     --生命
    self.roateDesire = 0.2          --旋转欲望 (0~1)
    self.turnRoundDesire = 0.4      --掉头欲望 (0~1)
    self.attackDesire = {3, 7}      --攻击欲望 (例:{3, 7}, 即攻击间隔3~7秒)
    self.moveSpeed = 150

    -- 当前状态
    self.isDie = false
    self.isCanMove = false
    self.isCanAttark = true
    self.currentSpeed = CSE.Vector2.zero

    -- 辅助属性
    self.lastRoateOrTurnRoundPoint = CSE.Vector2.zero

    -- 组件获取
    self.rb = self.gameObject:GetComponent('Rigidbody2D')
    self.anim = self.gameObject:GetComponent('Animator')
    self.boxColl = self.gameObject:GetComponent('BoxCollider2D')
end

function EmenyBase:OnEnable()
    self:AddListeners()
    self:WaitEmenyBirth()
end

function EmenyBase:OnDisable()
    self:RemoveListeners()
end

function EmenyBase:Update()
    self:EmenyAI()
end

function EmenyBase:FixedUpdate()
    if not self.isCanMove or self.isDie then
        return
    end

    self.currentSpeed:Set(self.transform.up.x * (self.moveSpeed * CSE.Time.fixedDeltaTime), self.transform.up.y * (self.moveSpeed * CSE.Time.fixedDeltaTime))
    self.rb.velocity = self.currentSpeed
end

function EmenyBase:OnTriggerEnter2D(other)
    if other:CompareTag('PlayerBullet') then
        -- 设置动画/状态
        self.isDie = true
        self.anim:SetBool(self.IS_DIE_ID, true)

        -- 速度设置为0
        self.rb.velocity = CSE.Vector2.zero

        -- 当前存活的Emeny -1
        GameManager.currentEmenyCount = GameManager.currentEmenyCount - 1

        -- 关闭碰撞器
        self.boxColl.enabled = false

        -- 动画结束销毁
        local waitDieAnimEnd = Coroutine.Create(bind(UnityUtils.WaitFormSecond), 0.5, function ()
            CSE.Object.Destroy(self.gameObject)
        end)
        self.host:StartCoroutine(waitDieAnimEnd)
    end
end

-- AI逻辑 :
-- # 移动:
--      1.每帧判断是否到达整数位点 (T:下一步AI, F:继续当前方向移动)
--      2.如果到达整数位点, 判断是否需要转向 (T:转向, F:继续当前方向移动)
-- # 攻击:
--      每帧判断是否到达攻击间隔 (T:攻击, F:继续当前方向移动)
function EmenyBase:EmenyAI()
    if not self.isCanMove or self.isDie then
        return
    end

    self:MoveAI()
    self:AttackAI()
end

function EmenyBase:MoveAI()
    if not self:IsInIntegerPoint() then
        return
    end

    -- CS.UnityEnhine
    local rayStartPoint1 = CSE.Vector2(self.transform.position.x, self.transform.position.y) + CSE.Vector2(self.transform.up.x, self.transform.up.y) * 0.75 + CSE.Vector2(self.transform.right.x, self.transform.right.y) * 0.75
    local rayStartPoint2 = CSE.Vector2(self.transform.position.x, self.transform.position.y) - CSE.Vector2(self.transform.up.x, self.transform.up.y) * 0.75 + CSE.Vector2(self.transform.right.x, self.transform.right.y) * 0.75
    local rayStartPoint3 = CSE.Vector2(self.transform.position.x, self.transform.position.y) + CSE.Vector2(self.transform.up.x, self.transform.up.y) * 0.75 - CSE.Vector2(self.transform.right.x, self.transform.right.y) * 0.75
    local rayStartPoint4 = CSE.Vector2(self.transform.position.x, self.transform.position.y) - CSE.Vector2(self.transform.up.x, self.transform.up.y) * 0.75 - CSE.Vector2(self.transform.right.x, self.transform.right.y) * 0.75

    local rHit1 = CSE.Physics2D.Raycast(rayStartPoint1, CSE.Vector2(self.transform.right.x, self.transform.right.y), 2, self.ROTATE_LAYER_MASK)
    local rHit2 = CSE.Physics2D.Raycast(rayStartPoint2, CSE.Vector2(self.transform.right.x, self.transform.right.y), 2, self.ROTATE_LAYER_MASK)
    local lHit1 = CSE.Physics2D.Raycast(rayStartPoint3, CSE.Vector2(-self.transform.right.x, -self.transform.right.y), 2, self.ROTATE_LAYER_MASK)
    local lHit2 = CSE.Physics2D.Raycast(rayStartPoint4, CSE.Vector2(-self.transform.right.x, -self.transform.right.y), 2, self.ROTATE_LAYER_MASK)

    -- Debug Ray
    if IS_EDITOR then
        CSE.Debug.DrawRay(CSE.Vector3(rayStartPoint1.x, rayStartPoint1.y, 0), CSE.Vector3(self.transform.right.x, self.transform.right.y, 0) * 2, rHit1.collider == nil and CSE.Color.green or CSE.Color.red)
        CSE.Debug.DrawRay(CSE.Vector3(rayStartPoint2.x, rayStartPoint2.y, 0), CSE.Vector3(self.transform.right.x, self.transform.right.y, 0) * 2, rHit2.collider == nil and CSE.Color.green or CSE.Color.red)
        CSE.Debug.DrawRay(CSE.Vector3(rayStartPoint3.x, rayStartPoint3.y, 0), CSE.Vector3(-self.transform.right.x, -self.transform.right.y, 0) * 2, lHit1.collider == nil and CSE.Color.green or CSE.Color.red)
        CSE.Debug.DrawRay(CSE.Vector3(rayStartPoint4.x, rayStartPoint4.y, 0), CSE.Vector3(-self.transform.right.x, -self.transform.right.y, 0) * 2, lHit2.collider == nil and CSE.Color.green or CSE.Color.red)
    end

    if CS.UnityEngine.Random.Range(0, 100) < self.roateDesire * 100 then
        local canRoateR = rHit1.collider == nil and rHit2.collider == nil
        local canRoateL = lHit1.collider == nil and lHit2.collider == nil

        if canRoateR and canRoateL then
            if CS.UnityEngine.Random.Range(0, 100) < 50 then
                self:MobileAdsorption()
                self:SetLastRoateOrTurnRoundPoint(self.transform.position)
                self.transform.eulerAngles = self.transform.eulerAngles - CSE.Vector3(0, 0, 90)
                return
            else
                self:MobileAdsorption()
                self:SetLastRoateOrTurnRoundPoint(self.transform.position)
                self.transform.eulerAngles = self.transform.eulerAngles + CSE.Vector3(0, 0, 90)
                return
            end
        end

        if canRoateR then
            self:MobileAdsorption()
            self:SetLastRoateOrTurnRoundPoint(self.transform.position)
            self.transform.eulerAngles = self.transform.eulerAngles - CSE.Vector3(0, 0, 90)

            return
        end

        if canRoateL then
            self:MobileAdsorption()
            self:SetLastRoateOrTurnRoundPoint(self.transform.position)
            self.transform.eulerAngles = self.transform.eulerAngles + CSE.Vector3(0, 0, 90)

            return
        end
    end

    local rayStartPoint5 = CSE.Vector2(self.transform.position.x, self.transform.position.y) + CSE.Vector2(self.transform.up.x, self.transform.up.y)
    local forwardHit = CSE.Physics2D.Raycast(rayStartPoint5, CSE.Vector2(self.transform.up.x, self.transform.up.y), 0.2, self.ROTATE_LAYER_MASK)
    if IS_EDITOR then
        CSE.Debug.DrawRay(CSE.Vector3(rayStartPoint5.x, rayStartPoint5.y, 0), self.transform.up * 0.8, rHit1.collider == nil and CSE.Color.green or CSE.Color.red)
    end
    if forwardHit.collider ~= nil then
        if CS.UnityEngine.Random.Range(0, 100) < self.turnRoundDesire * 100 then
            self:MobileAdsorption()
            self:SetLastRoateOrTurnRoundPoint(self.transform.position)
            self.transform.eulerAngles = self.transform.eulerAngles + CSE.Vector3(0, 0, 180)

            return
        end
    end
end

function EmenyBase:AttackAI()
    if not self.isCanAttark then
        return
    end
    self.isCanAttark = false
    local wait = Coroutine.Create(bind(UnityUtils.WaitFormSecond), CS.UnityEngine.Random.Range(self.attackDesire[1], self.attackDesire[2]), function ()
        -- 攻击
        self.isCanAttark = true
        ResManager.LoadGameObject('Assets/GameRes/Prefabs/Bullet/EmenyBullet.prefab', self.transform.position + self.transform.up, CSE.Quaternion.Euler(0, 0, self.transform.eulerAngles.z), function ()
            -- 初始化子弹
        end)
    end)
    self.host:StartCoroutine(wait)
end

function EmenyBase:IsInIntegerPoint()
    if math.abs(self.transform.position.x - self.lastRoateOrTurnRoundPoint.x) + math.abs(self.transform.position.y - self.lastRoateOrTurnRoundPoint.y) < 2 then
        return false
    end

    local _, posModfX = math.modf(self.transform.position.x)
    if posModfX >= 0.75 then
        return true
    elseif posModfX <= 0.25 then
        return true
    end

    local _, posModfY = math.modf(self.transform.position.y)
    if posModfY >= 0.75 then
        return true
    elseif posModfY <= 0.25 then
        return true
    end

    return false
end

function EmenyBase:MobileAdsorption()
    local pos = CSE.Vector3(self.transform.position.x, self.transform.position.y, self.transform.position.z)

    local _, posModfX = math.modf(self.transform.position.x)
    if posModfX >= 0.65 then
        pos.x = math.ceil(pos.x)
    elseif posModfX <= 0.35 then
        pos.x = math.floor(pos.x)
    end

    local _, posModfY = math.modf(self.transform.position.y)
    if posModfY >= 0.65 then
        pos.y = math.ceil(pos.y)
    elseif posModfY <= 0.35 then
        pos.y = math.floor(pos.y)
    end

    self.transform.position = pos
end

function EmenyBase:SetLastRoateOrTurnRoundPoint(position)
    self.lastRoateOrTurnRoundPoint = position
    if self.preventStuck ~= nil then
        self.host:StopCoroutine(self.preventStuck)
    end
    self.preventStuck = Coroutine.Create(bind(UnityUtils.WaitFormSecond), CS.UnityEngine.Random.Range(self.attackDesire[1], self.attackDesire[2]), function ()
            -- 防卡死
            self.lastRoateOrTurnRoundPoint = CSE.Vector2.zero
    end)
    self.host:StartCoroutine(self.preventStuck)
end

-- 自定义逻辑
function EmenyBase:WaitEmenyBirth()
    local waitEmenyBirthCoroutine = Coroutine.Create(bind(UnityUtils.WaitFormSecond), 0.5, function ()
        self.isCanMove = true
        self.anim:SetBool(self.IS_CAN_MOVE_ID, true)
    end)
    self.host:StartCoroutine(waitEmenyBirthCoroutine)
end

function EmenyBase:AddListeners()
    self:AddGameOverEvent()
end

function EmenyBase:RemoveListeners()
    EventManager:RemoveListener('GameOver', self.onGameOver)
end

--------- EventManager管理的事件
function EmenyBase:AddGameOverEvent()
    self.onGameOver = function ()
        CSE.Object.Destroy(self.gameObject, 0.1)
    end
    EventManager:AddListener('GameOver', self.onGameOver)
end

return EmenyBase